﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using HighDiveAdmin.Handlers;
using HighDiveEngine.Aggregates;
using HighDiveEngine.Model;
using HighDiveEngine.Resources;

namespace HighDiveAdmin.Engine
{
    public class IsAliveChecker
    {
        public static event DelegateVoidString EventJudgeDisconnected;

        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private Dictionary<Judge, DateTime> _lastHeardFrom;
        private Dictionary<Judge, bool> _isAlive;

        public IsAliveChecker()
        {
            _lastHeardFrom = new Dictionary<Judge, DateTime>();
            _isAlive = new Dictionary<Judge, bool>();
        }

        public void LoadJudges(JudgeList judgeList)
        {
            lock (_isAlive)
            {
                _isAlive.Clear();
                foreach (var judge in judgeList)
                    _isAlive.Add(judge, true);
            }

            lock (_lastHeardFrom)
            {
                _lastHeardFrom.Clear();
                foreach (var judge in judgeList)
                    _lastHeardFrom.Add(judge, DateTime.Now);
            }
        }

        public void AliveMessageReceived(string idString)
        {
            if (Log.IsDebugEnabled) { Log.Debug("Received JudgeIsAlive from " + idString); }
            lock (_isAlive)
            {
                _isAlive[_isAlive.Keys.First(x => x.IdString == idString)] = true;
            } 
            UpdateLastHeardFrom(idString);
        }

        public void UpdateLastHeardFrom(string idString)
        {
            if (Log.IsDebugEnabled) { Log.Debug("Updating lastheardfrom for " + idString); }
            lock (_lastHeardFrom)
            {
                _lastHeardFrom[_lastHeardFrom.Keys.First(x => x.IdString == idString)] = DateTime.Now;
            }
        }

        public void IsAliveThread()
        {
            var checkAlive = true;
            if (Log.IsInfoEnabled) { Log.Info("Starting IsAliveChecker Thread to ping Judge clients"); }
            while (checkAlive)
            {
                try
                {
                    lock (_lastHeardFrom)
                    {
                        foreach (var judge in _lastHeardFrom)
                        {
                            lock (judge.Key.Status)
                            {
                                if ((judge.Key.Status.Equals(JudgeStatus.Status.Connected) || judge.Key.Status.Equals(JudgeStatus.Status.SlowReply)) &&
                                                        judge.Value.AddMinutes(1).CompareTo(DateTime.Now) < 0)
                                {
                                    if (Log.IsInfoEnabled) { Log.Info("Pinging judge " + judge.Key.IdString);}
                                    TcpHandler.Instance.SendMessage(judge.Key.IpAdress + "@" + "IsAlive");
                                    lock (_isAlive)
                                    {
                                        _isAlive[judge.Key] = false;
                                    }
                                } 
                            }
                        }
                    }
                    Thread.Sleep(10000);

                    lock (_isAlive)
                    {
                        foreach (var judge in _isAlive)
                        {
                            var judgeDc = false;
                            lock (judge.Key.Status)
                            {
                                if (!judge.Key.Status.Equals(JudgeStatus.Status.Na) && !judge.Key.Status.Equals(JudgeStatus.Status.Disconnected) && !judge.Value)
                                {
                                    judge.Key.Status.SetTo(JudgeStatus.Status.Disconnected);
                                    judgeDc = true;
                                    if (Log.IsInfoEnabled) { Log.Info(judge.Key.IdString + " has disconnected.."); }
                                }
                            }
                            if (judgeDc)
                            {
                                if (EventJudgeDisconnected != null)
                                    EventJudgeDisconnected(judge.Key.IdString);
                            }
                        }
                    }
                    if (Log.IsInfoEnabled) { Log.Info("CheckAliveThread taking a nap"); }
                    Thread.Sleep(60000);
                }
                catch (ThreadAbortException)
                {
                    if (Log.IsInfoEnabled) { Log.Info("Aborting IsAliveChecker Thread"); }
                    checkAlive = false;
                }
            }
            if (Log.IsInfoEnabled) { Log.Info("Closing IsAliveChecker Thread"); }
        }
    }
}